package org.example.gradleauth.security;

import org.example.gradleauth.security.filter.SecurityLoginFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * SecurityConfiguration
 *
 * @author 1141193930@qq.com
 */
@Configuration
@EnableMethodSecurity
public class SecurityConfiguration {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        // @formatter:off
        http
                // 禁用 csrf
                .csrf(AbstractHttpConfigurer::disable)
                // 禁用 HTTP Basic 认证
                .httpBasic(AbstractHttpConfigurer::disable)
                //url  org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager.check
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/login","/token/**").permitAll()
                        .requestMatchers("/actuator/**").permitAll()
                        .anyRequest().authenticated()         // 其他所有请求都需要认证
                )
                //登录拦截器
                .addFilterAt(new SecurityLoginFilter(), UsernamePasswordAuthenticationFilter.class)
                //jwt
                .oauth2ResourceServer((oauth2)-> oauth2.jwt(Customizer.withDefaults()))
                .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .exceptionHandling((exceptions) -> exceptions
                        .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
                        .accessDeniedHandler(new BearerTokenAccessDeniedHandler())
                )
                //禁用 Spring Security 自带的表单登录功能
                .formLogin(AbstractHttpConfigurer::disable);

        // @formatter:on

        return http.build();
    }

    /**
     * 参考网址 ： https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/dao-authentication-provider.html
     * <p>
     * 配置并返回一个AuthenticationManager实例。
     * 该方法使用UserDetailsService和PasswordEncoder来创建一个DaoAuthenticationProvider，
     * 并将其设置为ProviderManager的提供者。
     *
     * @param userDetailsService 用户详细信息服务，用于加载用户信息
     * @param passwordEncoder    密码编码器，用于验证用户密码
     * @return 返回配置好的AuthenticationManager实例
     */
    @Bean
    public AuthenticationManager authenticationManager(
            UserDetailsService userDetailsService,
            PasswordEncoder passwordEncoder,
            DefaultAuthenticationEventPublisher defaultAuthenticationEventPublisher
    ) {
        // 创建一个DaoAuthenticationProvider实例
        //或继承DaoAuthenticationProvider创建一个自己的认证逻辑    （或AbstractUserDetailsAuthenticationProvider）
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();

        // 设置UserDetailsService，用于加载用户详细信息
        provider.setUserDetailsService(userDetailsService);

        // 设置PasswordEncoder，用于验证用户密码
        provider.setPasswordEncoder(passwordEncoder);

        // 设置是否隐藏用户未找到异常
//        provider.setHideUserNotFoundExceptions(false);

        ProviderManager providerManager = new ProviderManager(provider);
        //注册默认的监听器
        providerManager.setAuthenticationEventPublisher(defaultAuthenticationEventPublisher);

        // 返回一个ProviderManager实例，使用上述配置的provider
        return providerManager;
    }

}
